package br.usp.ime.ingpos.web.interceptors;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import br.com.caelum.vraptor.Intercepts;
import br.com.caelum.vraptor.Result;
import br.com.caelum.vraptor.core.InterceptorStack;
import br.com.caelum.vraptor.interceptor.Interceptor;
import br.com.caelum.vraptor.ioc.RequestScoped;
import br.com.caelum.vraptor.resource.ResourceMethod;
import br.usp.ime.ingpos.modelo.TipoPerfil;
import br.usp.ime.ingpos.modelo.Usuario;
import br.usp.ime.ingpos.web.controllers.IndexController;
import br.usp.ime.ingpos.web.controllers.LoginController;
import br.usp.ime.ingpos.web.controllers.UsuarioSessao;

@Intercepts
@RequestScoped
public class LoginInterceptor
    implements
        Interceptor
{
    private final Result result;
    private final UsuarioSessao usuarioSessao;

    public LoginInterceptor(
        final Result result,
        final UsuarioSessao usuarioSessao )
    {
        this.result = result;
        this.usuarioSessao = usuarioSessao;
    }

    public boolean accepts(
        ResourceMethod method )
    {
        // Aceita todos os metodos de controllers para verificar se o usuario
        // est� autenticado
        return true;
    }

    public void intercept(
        InterceptorStack stack,
        ResourceMethod method,
        Object resourceInstance )
    {
        if( usuarioSessao.isUsuarioAutenticado() ) {
            if( ! bloqueadoParaAcessarMetodo( method, usuarioSessao.getUsuario() ) ) {
                stack.next( method, resourceInstance );
            } else {
                result.redirectTo( IndexController.class ).index();
            }

        } else if( desejaAcessarMetodoIrrestrito( method ) ) {
            stack.next( method, resourceInstance );
        } else {
            result.redirectTo( LoginController.class ).login();
        }
    }

    private boolean desejaAcessarMetodoIrrestrito(
        final ResourceMethod method )
    {
        // Verifica se metodo ou classe foram anotados com IrrestrictAcess
        return method.getMethod().isAnnotationPresent( IrrestrictAccess.class )
            || method.getResource().getType().isAnnotationPresent( IrrestrictAccess.class );
    }

    private boolean bloqueadoParaAcessarMetodo(
        final ResourceMethod method,
        final Usuario usuario )
    {
        if( method.getMethod().isAnnotationPresent( RestrictAccessPerfil.class )
            || method.getResource().getType().isAnnotationPresent( RestrictAccessPerfil.class ) ) {

            Set<TipoPerfil> perfisBloqueados = new HashSet<TipoPerfil>();

            final RestrictAccessPerfil anotacoesDaClasse = method.getResource().getType().getAnnotation(
                RestrictAccessPerfil.class );
            final RestrictAccessPerfil anotacoesDoMetodo = method.getMethod().getAnnotation(
                RestrictAccessPerfil.class );

            if( anotacoesDaClasse != null )
                perfisBloqueados.addAll( Arrays.asList( anotacoesDaClasse.values() ) );
            if( anotacoesDoMetodo != null )
                perfisBloqueados.addAll( Arrays.asList( anotacoesDoMetodo.values() ) );

            return perfisBloqueados.contains( usuario.getPerfil().getPerfilID() );

        }
        return false;
    }
}
